package cc.shacocloud.mirage.utils;

import cc.shacocloud.mirage.utils.map.ConcurrentReferenceHashMap;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.IOException;
import java.net.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.Objects;

/**
 * 基于 {@link URLClassLoader} 的缓存类加载器
 *
 * @author 思追(shaco)
 */
public class CacheURLClassLoader extends URLClassLoader {
    
    public static final URL[] EMPTY_URL_ARRAY = new URL[0];
    
    private final Map<String, Class<?>> classCache;
    
    public CacheURLClassLoader() {
        this(EMPTY_URL_ARRAY);
    }
    
    public CacheURLClassLoader(URL[] urls) {
        this(urls, null);
    }
    
    public CacheURLClassLoader(@Nullable ClassLoader parent) {
        this(EMPTY_URL_ARRAY, parent);
    }
    
    public CacheURLClassLoader(URL[] urls, @Nullable ClassLoader parent) {
        this(urls, parent, 1024);
    }
    
    public CacheURLClassLoader(URL[] urls, @Nullable ClassLoader parent, int initialCapacity) {
        super(urls, parent);
        this.classCache = new ConcurrentReferenceHashMap<>(initialCapacity);
    }
    
    
    /**
     * 向这个类加载器添加一个类路径
     *
     * @param path 是 jar 文件或目录
     * @see #addURL(URL)
     */
    public void addClasspath(final String path) {
        AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
            
            URI newURI;
            try {
                newURI = new URI(path);
                // 检查我们是否可以从该 URI 创建一个 URL
                newURI.toURL();
            } catch (URISyntaxException | IllegalArgumentException | MalformedURLException e) {
                // URI 的格式是错误的，所以让我们用文件试试吧......
                newURI = new File(path).toURI();
            }
            
            URL[] urls = getURLs();
            for (URL url : urls) {
                try {
                    if (newURI.equals(url.toURI())) return null;
                } catch (URISyntaxException e) {
                    throw new RuntimeException(e);
                }
            }
            try {
                addURL(newURI.toURL());
            } catch (MalformedURLException e) {
                throw new RuntimeException(e);
            }
            return null;
        });
    }
    
    @Override
    protected synchronized Class<?> findClass(String name) throws ClassNotFoundException {
        String className = normalize(name);
        
        Class<?> clazz = classCache.get(className);
        if (Objects.isNull(clazz)) {
            clazz = super.findClass(className);
            classCache.put(className, clazz);
        }
        
        return clazz;
    }
    
    /**
     * 规范化，即：内部类未使用 $ 符号链接的转为使用 $ 链接
     * <p>
     * 内部类判断逻辑：从后往前找到开头大写的路径作为对象，第一个之后的都作为内部类进行链接
     */
    protected String normalize(String fullClassName) {
        if (Objects.isNull(fullClassName) || fullClassName.isBlank()) return fullClassName;
        
        StringBuilder builder = new StringBuilder();
        
        boolean inner = false;
        for (String p : fullClassName.split("\\.")) {
            
            if (builder.length() != 0) {
                builder.append(inner ? "$" : ".");
            }
            
            builder.append(p);
            
            if (Character.isUpperCase(p.charAt(0))) {
                inner = true;
            }
        }
        
        return builder.toString();
    }
    
    @Override
    public void close() throws IOException {
        super.close();
        this.classCache.clear();
    }
}
